﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using YetAnotherChat.TcpServer.WebSocketProtocols;

namespace YetAnotherChat.TcpServer
{
    /// <summary>
    /// ServerConnection wraps around TcpClient that server use to send/receive msg to websocket browser.
    /// </summary>
    public class ServerConnection: Connection
    {
        /// <summary>
        /// A link to the server listener instance this client is currently hosted on.
        /// </summary>
        public WebSocketServer Server;

        public ServerConnection(WebSocketServer server, TcpClient connection, WebSocketProtocolFactory webSocketProtocolFactory)
            : base(connection, webSocketProtocolFactory)
        {
            Server = server;
        }

        protected override void OnMessageSent(Connection con)
        {
            Server.OnMessgeSent(new MessageEventArgs(con as ServerConnection));
        }

        protected override void OnClientDisconnecting()
        {
            Server.OnClientDisconnecting(new ConnectionEventArgs(this));
        }

        protected override void CleanUpConnection(Connection con)
        {
            Server.RemoveConnection(con);
        }

        protected override void ProcessReceivedMessage()
        {
            if (!IsSetup)
            {
                Debug.WriteLine(string.Format("This is assumed first received request to connect, handle protocol selection," +
                    " authentication."));
                _protocol = _protocolFactory.ProcessConnectRequest(this);

                if (_protocol == null)
                {
                    Debug.WriteLine(string.Format("Can't find protocol, send not supported msg and disconect"));
                    Send(NotImplemented, true, true);
                    return;
                }

                if (!_protocol.CheckAuthentication(this))
                {
                    Debug.WriteLine(string.Format("Can't authenticate request, send not supported msg and disconect"));
                    Send(NotAuthenticated, true, true);
                }
            }
            else
            {
                Debug.WriteLine(string.Format("Start handle some data frame"));
                _protocol.DataFrame.Append(Buffer, true);
                if (_protocol.DataFrame.Length <= MaxFrameSize)
                {
                    switch (_protocol.DataFrame.State)
                    {
                        case DataFrameBase.DataState.Complete:
                            Debug.WriteLine(string.Format("Received some data frame"));
                            Server.OnMessageReceived(new MessageEventArgs(this));
                            break;
                        case DataFrameBase.DataState.Closed:
                            Debug.WriteLine(string.Format("Closing data frame"));
                            Send(_protocol.DataFrame.CreateInstance(), false, true);
                            break;
                        case DataFrameBase.DataState.Ping:
                            Debug.WriteLine(string.Format("Ping data frame"));
                            _protocol.DataFrame.State = DataFrameBase.DataState.Complete;
                            DataFrameBase dataFrame = _protocol.DataFrame.CreateInstance();
                            dataFrame.State = DataFrameBase.DataState.Pong;
                            List<ArraySegment<byte>> pingData = _protocol.DataFrame.AsRaw();
                            foreach (var item in pingData)
                            {
                                dataFrame.Append(item.Array);
                            }
                            Send(dataFrame);
                            break;
                        case DataFrameBase.DataState.Pong:
                            Debug.WriteLine(string.Format("Pong data frame"));
                            _protocol.DataFrame.State = DataFrameBase.DataState.Complete;
                            break;
                    }
                }
            }
        }
    }
}
